package com.butterfly.encrypt.util;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.log4j.Logger;

/**
 * 用密钥进行DES加密，解密的工具类
 * @author fox email:hitman__47@163.com
 * @version 1.0 2013-10-28
 *
 */
public class Des {
	private static final  Logger log = Logger.getLogger(Des.class);
	/**
	 * 将指定内容用主密钥进行DES加密，并返回DES加密结果
	 * @param binfo
	 * 					加密内容(byte[])
	 * @param bDesKey
	 * 					密钥(byte[])
	 * @return	
	 * 					DES加密结果(byte[])
	 * @throws BadPaddingException 
	 * @throws IllegalBlockSizeException 
	 * @throws NoSuchPaddingException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidKeyException 
	 */
	public static byte[] encryptDes(byte binfo[],byte[] bDesKey) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
		return cipher(binfo,bDesKey,Cipher.ENCRYPT_MODE,"DES/ECB/PKCS5Padding");
	}
	/**
	 * 将指定内容用主密钥进行DES解密，并返回DES解密结果
	 * @param binfo
	 * 					加密内容
	 * @param bDesKey
	 * 					密钥
	 * @return
	 * 					 DES解密结果
	 * @throws BadPaddingException 
	 * @throws IllegalBlockSizeException 
	 * @throws NoSuchPaddingException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidKeyException 
	 */
	public static byte[] decryptDes(byte binfo[],byte[] bDesKey) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
		return cipher(binfo,bDesKey,Cipher.DECRYPT_MODE,"DES/ECB/PKCS5Padding");
		
	}
	/**
	 * 操作加密或解密数据，按模式参数决定加密还是解密
	 * @param binfo
	 * 				需要进行加解密的数据
	 * @param bDesKey
	 * 				 密钥
	 * @param mode 
	 * 				加密或者解密模式
	 * @param transformation
	 * 				transformation - 转换的名称，例如 DES/CBC/PKCS5Padding。
	 * @return
	 * @throws NoSuchPaddingException 
	 * @throws NoSuchAlgorithmException 
	 * @throws InvalidKeyException 
	 * @throws BadPaddingException 
	 * @throws IllegalBlockSizeException 
	 */
	public static byte[] cipher(byte binfo[],byte bDesKey[],int mode,String transformation) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
		String algs[]=transformation.split("/");
		/**
		 * 根据给定的字节数组构造一个密钥。
		 * 此构造方法不检查给定的字节实际上是否指定了一个指定算法的密钥。
		 * 例如，如果算法是 DES，此构造方法不检查 key 是否是 8 字节长，并且也不检查是否是全弱或者半弱密钥。
		 * 为了执行这些检查，应当使用一个特定于算法的密钥规范（在此情况下是：DESKeySpec）。
		 * 参数：
		 * 		 key - 密钥的密钥内容。复制该数组的内容来防止后续修改。
		 *		 algorithm - 与给定的密钥内容相关联的密钥算法的名称。
		 */
		SecretKey key = new SecretKeySpec(bDesKey, algs[0]);
		/**
		 * 生成一个实现指定转换的 Cipher对象。 
		 * 如果默认的提供程序包提供了请求的转换实现，则会返回包含该实现的一个 Cipher 实例。
		 * 如果默认的提供程序包中没有可用的转换，则将搜索其他的提供程序包。 
		 *参数：
		 *		transformation - 转换的名称，例如 DES/CBC/PKCS5Padding。
		 *返回：
		 *		实现所请求转换的 cipher 
		 */
		Cipher cipher = Cipher.getInstance(transformation);
		/**
		 * 用密钥初始化此 cipher。
		 * 为以下 4 种操作之一初始化该 cipher：加密、解密、密钥包装或密钥打开，这取决于 mode 的值。 
		 *参数：
		 *		mode - 此 cipher 的操作模式（其为如下之一：ENCRYPT_MODE、DECRYPT_MODE、WRAP_MODE 或 UNWRAP_MODE）
		 *		key - 密钥	
		 */
		cipher.init(mode, key);
		/**
		 * 按单部分操作加密或解密数据，或者结束一个多部分操作。数据被加密还是解密取决于此 cipher 的初始化方式。
		 * 处理 input 缓冲区中的字节以及可能在上一次 update 操作中已缓存的任何输入字节，其中应用了填充（如果请求）。结果存储在新缓冲区中。
		 * 结束时，此方法将把此 cipher 对象重置为上一次调用 init 初始化得到的状态。
		 * 即重置该对象，可供加密或解密（取决于调用 init 时指定的操作模式）更多的数据。
		 * 参数：
		 * 		input - 输入缓冲区
		 * 返回：
		 * 	            存储结果的新缓冲区 
		 */
		return cipher.doFinal(binfo);
	}
	/**
	 * 读取desKey
	 * @param DESKeyPath
	 * 					 密钥路径
	 * @return
	 * 					 密钥的字节流数据
	 */
	public static byte[] getDesKey(String DESKeyPath){
		byte[] bDesKey=new byte[8];
		bDesKey=Des.getKeyFromLocalFile(DESKeyPath,bDesKey);
		return bDesKey;
	}
	/**
	 * 从DES密钥文件中读取出密钥，放入字节流对象
	 * @param keyFilePath
	 * 						DES密钥文件目录
	 * @param key
	 * 			  DES密钥文件存放的字节流对象
	 * @return    DES密钥文件读取出，放入字节流对象
	 */
	private static byte[] getKeyFromLocalFile(String keyFilePath, byte[] key) {
		 keyFilePath=keyFilePath.trim();
		 if("".equals(keyFilePath)){
			System.out.println("keyFilePath is null! "); 
			log.error("keyFilePath is null! ");
			return null;
		 }else if(8 != key.length&& 16!=key.length){
			 System.out.println("key length is invalid!");
			 log.error("key length is invalid!");
			 return null;
		 }
		 FileInputStream fis=null;
		 try {
			fis=new FileInputStream(keyFilePath);
			fis.read(key);
		 } catch (FileNotFoundException e) {
			log.error(e.getMessage(),e);
		 } catch (IOException e) {
			log.error(e.getMessage(),e);
		} finally{
			if(fis!=null){
				try {
					fis.close();
				} catch (IOException e) {
					log.error(e.getMessage(),e);
				}
			}
			
		}
		return key;
	}
	
}
